home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1833 / 1833.xpi / modules / yoonoStorage.js < prev    next >
Text File  |  2009-12-16  |  11KB  |  324 lines

  1. var EXPORTED_SYMBOLS = ["YOONO_STORAGE"];
  2.  
  3. // Globals
  4. const CI = Components.interfaces;
  5. const CL = Components.classes;
  6.  
  7. /////
  8. const YOONO_DIRSERVICE = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties);
  9. const CONSOLESERVICE = CL["@mozilla.org/consoleservice;1"].getService(CI.nsIConsoleService);
  10.  
  11. const PERMS_FILE      = 0655;
  12. /////
  13.  
  14. var yoono = {};
  15.  
  16. const YOONO_DB_REQUIRED_VERSION = 1;
  17.  
  18. function YoonoStorage() {
  19.   this.dataBase = null;
  20.   this.mainThread = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread;
  21.   this.backgroundThread = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0);
  22.   this.dbFileName = "";
  23. }
  24.  
  25. YoonoStorage.prototype.init = function (y) {
  26.   yoono = y;
  27. }
  28.  
  29. /**
  30.  *
  31.  * @param aStatement string for the statement, with placeholders ?1, ?2, etc
  32.  * @param aValues array of arrays , each holding the values to bind to the placeholders for one execution of the statement
  33.  */
  34. YoonoStorage.prototype.executeSql = function(aStatement, aDataSet) {
  35.   // Init the database the first time it's needed
  36.   var statusObject = {};
  37.   var statement = null;
  38.   statusObject.errorString = "";
  39.   statusObject.errorCode = 0;
  40.    if(!this.dataBase) {
  41.     this.openStorage();   // is that a good idea ?
  42.   }
  43.   if(!this.dataBase) {
  44.     statusObject.errorString = "Unable to open the database file";
  45.     statusObject.errorCode = 14;
  46.     yoono.log.error("Unable to execute : "+aStatement+"\nbecause DB is not open!");
  47.     return [statusObject];
  48.   }
  49.   try {
  50.     statement = this.dataBase.createStatement(aStatement);
  51.   } catch(e) {
  52.     try {
  53.       statusObject.errorString = this.dataBase.lastErrorString;
  54.       statusObject.errorCode = this.dataBase.lastError;
  55.     } catch(e1) {
  56.       statusObject.errorString = e1.message || e1;
  57.       statusObject.errorCode = -1;
  58.     }
  59.     yoono.log.error("Bad SQL query : "+aStatement+"\nbecause of : "+statusObject.errorString+" (" + statusObject.errorCode + ")\n");
  60.     return [statusObject];
  61.   }
  62.   if(!aDataSet) aDataSet = [[]]; // all statements don't need values... But they need to be executed at least once...
  63.   var nbDataSet = aDataSet.length; // always at least 1
  64.   // The VACUUM statement can't be performed from within a transaction
  65.   if('vacuum' != aStatement) {
  66.     try {
  67.       this.dataBase.beginTransaction(); // will fail if a transaction is already running
  68.     } catch(e) {
  69.       if(0 != this.dataBase.lastError) {
  70.         statusObject.errorString = this.dataBase.lastErrorString;
  71.         statusObject.errorCode = this.dataBase.lastError;
  72.       } else {
  73.         statusObject.errorString = e.message;
  74.         statusObject.errorCode = -1;
  75.       }
  76.       yoono.log.error("Unable to start sql transaction : "+e);
  77.       return [statusObject];
  78.     }
  79.   }
  80.  
  81.   var resultSets = [];
  82.   var rollback = false;
  83.   var startTime = 0, endTime = 0;
  84.  
  85.   if (yoono.prefs.get("debug.db")) {
  86.     startTime = new Date().getTime();
  87.     yoono.log.debug("Executing SQL " + nbDataSet + " times the statement : " + aStatement );
  88.   }
  89.   /*
  90.   var lowerCaseStatement = aStatement.toLowerCase();
  91.   var oneStepStatement = (0 == lowerCaseStatement.indexOf('insert'))
  92.                          || (0 == lowerCaseStatement.indexOf('delete'))
  93.                          || (0 == lowerCaseStatement.indexOf('update'))
  94.   */
  95.   try {
  96.     for(var idx = 0 ; idx < nbDataSet; idx ++) {
  97.       var resultSet = [];
  98.       // Number of data in this dataset = number of placeholders to bind
  99.       var nbData = aDataSet[idx].length;
  100.       if (nbData != statement.parameterCount) {
  101.         yoono.log.error("Warning : this SQL query -> <<"+aStatement+">> does'nt get his placeholders correctly set. "+nbData+" instead of "+statement.parameterCount+"!");
  102.       }
  103.       // set the values for place holders, if any
  104.  
  105.       for(var jdx = 0 ; jdx < nbData ; jdx ++) {
  106.         var aData = aDataSet[idx][jdx];
  107.         if(null == aData) {
  108.           statement.bindNullParameter(jdx);          
  109.         } else {
  110.           statement.bindUTF8StringParameter(jdx, aData);
  111.         }
  112.       }
  113.       // Execute the statement
  114.       var notDone = true;
  115.       do {
  116.         try {
  117.           notDone = statement.executeStep();
  118.         } catch (e) {
  119.           statusObject.errorString = this.dataBase.lastErrorString;
  120.           statusObject.errorCode = this.dataBase.lastError;
  121.           yoono.log.error("Error on executeStep for SQL query : "+aStatement+"\niteration " + idx + " of " + nbDataSet + " because of : "
  122.             + statusObject.errorString
  123.             + " (" + statusObject.errorCode + ")\nor because of : "+e+"\n"
  124.             + "Data : " + aDataSet[idx].join(' , '));
  125.           break;
  126.         }
  127.         if(!notDone) break;
  128.         var result = []; // one line of result
  129.         var column = null;
  130.  
  131.         for(var offset = 0; offset < statement.columnCount; offset++) {
  132.           column = statement.getUTF8String(offset);
  133.           result.push(column);
  134.         }
  135.         // If no results, do not put an empty array in the results
  136.         if(statement.columnCount)
  137.           resultSet.push(result);
  138.         // if(oneStepStatement) notDone = false;
  139.       }
  140.       while(notDone);
  141.  
  142.       resultSets.push(resultSet);
  143.       statement.reset();
  144.     }
  145.   } catch(e) {
  146.     if(0 != this.dataBase.lastError) {
  147.       statusObject.errorString = this.dataBase.lastErrorString;
  148.       statusObject.errorCode = this.dataBase.lastError;
  149.     } else {
  150.       statusObject.errorString = e.message;
  151.       statusObject.errorCode = -1;
  152.     }
  153.     yoono.log.error("Unable to execute SQL query : "+aStatement+"\niteration " + idx + " because of : "
  154.             + this.dataBase.lastErrorString
  155.             + " (" + this.dataBase.lastError + ")\nor because of : "+e+"\n");
  156.  
  157.     yoono.log.error("Data offset " + idx + " on " + nbData + " : " + aDataSet[idx].join(' , '));
  158.   }
  159.  
  160.   try {
  161.     statement.finalize();
  162.   } catch(e) {
  163.     yoono.log.error("Unable to execute finalize for statement : "+aStatement+"\nbecause of : "
  164.             + this.dataBase.lastErrorString
  165.             + " (" + this.dataBase.lastError + ")\nor because of : "+e+"\n");
  166.  
  167.     if(0 != this.dataBase.lastError) {
  168.       statusObject.errorString = this.dataBase.lastErrorString;
  169.       statusObject.errorCode = this.dataBase.lastError;
  170.     } else {
  171.       statusObject.errorString = e.message;
  172.       statusObject.errorCode = -1;
  173.     }
  174.   }
  175.   // No transaction to commit or rollback if running a vacuum statement
  176.   if('vacuum' != aStatement) {
  177.     try {
  178.       if(rollback) {
  179.         this.dataBase.rollbackTransaction();
  180.       } else {
  181.         this.dataBase.commitTransaction();
  182.       }
  183.       statusObject.lastInsertRowID = this.dataBase.lastInsertRowID ;
  184.     } catch(e) {
  185.       yoono.log.error("Unable to execute rollback or commit for statement : "+aStatement+"\nbecause of : "
  186.               + this.dataBase.lastErrorString
  187.               + " (" + this.dataBase.lastError + ")\nor because of : "+e+"\n");
  188.  
  189.       if(0 != this.dataBase.lastError) {
  190.         statusObject.errorString = this.dataBase.lastErrorString;
  191.         statusObject.errorCode = this.dataBase.lastError;
  192.       } else {
  193.         statusObject.errorString = e.message;
  194.         statusObject.errorCode = -1;
  195.       }
  196.     }
  197.   }
  198.   if (yoono.prefs.get("debug.db")) {
  199.     endTime = new Date().getTime();
  200.     yoono.log.debug("Executing SQL took : "+ (endTime - startTime)+"\n");
  201.   }
  202.   resultSets.unshift(statusObject)
  203.   return(resultSets);
  204. }
  205.  
  206. YoonoStorage.prototype.hasErrorOccured = function() {
  207.   return(this.dataBase.lastError && (this.dataBase.lastError < 100));
  208. }
  209. /**
  210.  *
  211.  * @param aStatement
  212.  * @param aDataset
  213.  * @param aCallback : object with property 'callback' holding the actuall callback function (that's because of IE)
  214.  */
  215. YoonoStorage.prototype.executeAsyncSql = function(aStatement, aDataset, aCallback) {
  216.   var storage=this;
  217.   storage.backgroundThread.dispatch( {
  218.       run: function() {
  219.         var results = storage.executeSql(aStatement,aDataset);
  220.  
  221.         storage.mainThread.dispatch({
  222.           run: function () {
  223.             if (aCallback && (typeof aCallback.callback=="function"))
  224.               aCallback.callback(results);
  225.           }
  226.         }, storage.mainThread.DISPATCH_NORMAL);
  227.       }
  228.     }, storage.backgroundThread.DISPATCH_NORMAL);
  229. }
  230.  
  231. YoonoStorage.prototype.closeStorage = function() {
  232.   if (!this.dataBase)
  233.     return;
  234.   this.dataBase.close();
  235.   this.dataBase = null;
  236. }
  237.  
  238. /**
  239.  * Backup the DB before a schema migration.
  240.  * If backup already exists, it's not overwritten
  241.  * @param aVersion
  242.  */
  243. YoonoStorage.prototype.backupDB = function(aVersion) {
  244.   var dbFile = this.getDBFile();
  245.   var path = YOONO_DIRSERVICE.get("ProfDS", CI.nsIFile)
  246.   path.append('yoono');
  247.   var newNameElts = this.dbFileName.split('.');
  248.   newNameElts[0] += '_' + aVersion + '.';
  249.   dbFile.copyTo(path, newNameElts.join(''));
  250. }
  251.  
  252. YoonoStorage.prototype.openStorage = function() {
  253.   if (this.dataBase)
  254.     return;
  255.   var dbFile = this.getDBFile();
  256.   if(!dbFile.exists()) {
  257.     this.createAndOpenStorage(dbFile);
  258.   } else {
  259.     this.openDBStorage(dbFile);
  260.   }
  261. }
  262.  
  263. YoonoStorage.prototype.getCurrentDBFileName = function() {
  264.   return(this.dbFileName);
  265. }
  266.  
  267. YoonoStorage.prototype.getDBFile = function() {
  268.   var dbFile = YOONO_DIRSERVICE.get("ProfDS", CI.nsIFile)
  269.   dbFile.append('yoono');
  270.   var userCredentials = yoono.main.getUserCredential();
  271.   var dbName = 'yoono_friends_db.sqlite';
  272.   if(userCredentials) {
  273.     if(userCredentials.anonymous) {
  274.       dbName = userCredentials.userId + '_friends_db.sqlite';
  275.     } else {
  276.       dbName = yoono.main.escapeFileName(userCredentials.login) + '_friends_db.sqlite';
  277.     }
  278.   }
  279.   dbFile.append(dbName);
  280.   // Fix some weird bug in yoono desktop 6.2 -> 7 migration
  281.   if (dbFile.exists() && dbFile.isDirectory())
  282.     dbFile.remove(true);
  283.   this.dbFileName = dbName;
  284.   return(dbFile);
  285. }
  286.  
  287. /**
  288.  * This method closes the database, renames the current file to the new file, and reopens the database
  289.  */
  290. YoonoStorage.prototype.switchDBStorage = function() {
  291.   this.closeStorage();
  292.   var currentFileName = this.getCurrentDBFileName();
  293.   var filePath = YOONO_DIRSERVICE.get("ProfDS", CI.nsIFile)
  294.   filePath.append('yoono');
  295.   var currentFile = filePath.clone();
  296.   var newFilePath = filePath.clone();
  297.   currentFile.append(currentFileName);
  298.   var aFile = this.getDBFile();
  299.   var newFileName = this.getCurrentDBFileName(); // was updated by getDBFile()
  300.   newFilePath.append(newFileName);
  301.   if(newFilePath.exists()) {
  302.     newFilePath.remove(false);
  303.   }
  304.   if(!currentFileName) {
  305.     this.createAndOpenStorage(newFilePath);
  306.   } else {
  307.     currentFile.moveTo(filePath, newFileName);
  308.     this.openDBStorage(aFile);
  309.   }
  310. }
  311.  
  312. YoonoStorage.prototype.openDBStorage = function(aFile) {
  313.   var storageService = CL["@mozilla.org/storage/service;1"].getService(CI.mozIStorageService);
  314.   // Open sqlite DB in unshared mode for FTS3
  315.   this.dataBase = storageService.openUnsharedDatabase(aFile);
  316. }
  317.  
  318. YoonoStorage.prototype.createAndOpenStorage = function(aFile) {
  319.   aFile.create(CI.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
  320.   this.openDBStorage(aFile);
  321. }
  322.  
  323. var YOONO_STORAGE = new YoonoStorage();
  324.